//PJAX <?php 
/*
 * PJAX Simple jQuery Plugin
 * Source code edited and improved by Olivier St-Laurent, Original source from jQuery-PJAX by Chris Wanstrath : https://github.com/defunkt/jquery-pjax
 * 
 * Changes the behavior of links to load content in ajax instead of reloading the page. 
 * 
 * Usage : 
 * 
 *      // To Initialise with all links and using Body as the Container : 
 *      
 *          $('a').pjax('body');
 *      
 *      
 *      // Now, all you have to do is to make sure that your content only show the inside of the Body element instead of the whole html document, when PJAX is used. 
 *      //   To do so, use the HTTP Request Header PJAX to verify if its called by PJAX, like this : !empty($_SERVER['HTTP_X_PJAX'])
 *      // Also, you may define the page's title inside the Body content in every request instead of inside the head tag, so the title can change on every page. 
 *      // 
 *      // Example : 
 *      
 *          define('PJAX',(!empty($_SERVER['HTTP_X_PJAX'])));
 *          
 *          <?php if (!PJAX) { ?>
 *              <!DOCTYPE html>
 *              <html>
 *                  <head>
 *                  </head>
 *                  <body>
 *          <?php } ?>
 *          
 *                      <title>My Website - Home page</title>
 *                      
 *                      ...... Content Here ......
 *                      
 *                      
 *          <?php if (!PJAX) { ?>
 *                  </body>
 *              </html>
 *          <?php } ?>
 *          
 */
//?>

var PjaxBeforeLoad = null;
var PjaxDefaultContainer = null;

(function($) {
    $.fn.pjax = function(container, options) {
        if (PjaxDefaultContainer === null) {
            PjaxDefaultContainer = container;
        }
        options = optionsFor(container, options);
        return this.click(function(event) {
            return handleClick(event, options);
        });
    };

    function handleClick(event, container, options) {
        options = optionsFor(container, options);

        var link = event.currentTarget;

        // Ctrl+click, Middle click and cmd click should open links in a new tab as normal. 
        if (event.which > 1 || event.metaKey) 
            return;
        
        // Ignore target="_blank"
        if (link.target === '_blank') {
            return;
        }

        // Ignore cross origin links
        if (location.protocol !== link.protocol || location.host !== link.host) 
            return;

        // Ignore anchors (Hash tags) 
        if (link.hash && link.href.replace(link.hash, '') === location.href.replace(location.hash, '')) 
            return;
        
        // Ignore empty anchors 
        if (link.href === location.href+'#') 
            return;

        var defaults = {
            url: link.href,
            container: $(link).attr('data-pjax'),
            target: link,
            fragment: null
        };
        
        if (typeof PjaxBeforeLoad === 'function') PjaxBeforeLoad();
        
        $.pjax($.extend({}, defaults, options));

        event.preventDefault();
        return false;
    }

    function parseURL(url) {
        var a = document.createElement('a');
        a.href = url;
        return a;
    }

    var pjax = $.pjax = function(options) {
        options = $.extend(true, {}, $.ajaxSettings, pjax.defaults, options);

        if ($.isFunction(options.url)) {
            options.url = options.url();
        }

        var target = options.target;

        var url = options.url;
        var hash = parseURL(url).hash;

        var context = options.context = findContainerFor(options.container);

        function fire(type, args) {
            var event = $.Event(type, {relatedTarget: target});
            context.trigger(event, args);
            return !event.isDefaultPrevented();
        }

        var timeoutTimer;

        options.beforeSend = function(xhr, settings) {
            url = settings.url;
            
            if (settings.timeout > 0) {
                timeoutTimer = setTimeout(function() {
                    if (fire('pjax:timeout', [xhr, options])) {
                        xhr.abort('timeout');
                    }
                }, settings.timeout);
                settings.timeout = 0;
            }

            if (!options['noPjaxHeader']) {
                xhr.setRequestHeader('X-PJAX', 'true');
            }

            var result;

            if (!fire('pjax:beforeSend', [xhr, settings]))
                return false;

            fire('pjax:start', [xhr, options]);
        };
        
        options.complete = function(xhr, textStatus) {
            if (timeoutTimer) {
                clearTimeout(timeoutTimer);
            }
            
            fire('pjax:complete', [xhr, textStatus, options]);
            fire('pjax:end', [xhr, options]);
            if (typeof pjaxAfterLoadingCallback === 'function') {
                pjaxAfterLoadingCallback();
            }
        };

        options.error = function(xhr, textStatus, errorThrown) {
            var respUrl = xhr.getResponseHeader('X-PJAX-URL');
            if (respUrl) {
                url = respUrl;
            }

            var allowed = fire('pjax:error', [xhr, textStatus, errorThrown, options]);
            if (textStatus !== 'abort' && allowed) {
                window.location = url;
            }
        };
        
        options.success = function(data, status, xhr) {
            var respUrl = xhr.getResponseHeader('X-PJAX-URL');
            if (respUrl) {
                url = respUrl;
            }
            
            var title, oldTitle = document.title;

            if (options.fragment) {
                // If they specified a fragment, look for it in the response and pull it out.
                var html = $('<html>').html(data);
                var $fragment = html.find(options.fragment);
                if ($fragment.length) {
                    this.html($fragment.contents());
                    // If there's a <title> tag in the response, use it as the page's title. Otherwise, look for data-title and title attributes.
                    title = html.find('title').text() || $fragment.attr('title') || $fragment.data('title');
                } else {
                    return window.location = url;
                }
            } else {
                // If we got no data or an entire web page, go directly
                // to the page and let normal error handling happen.
                if (!$.trim(data) || /^\s*((<!DOCTYPE)|(<html))/i.test(data)) {
                    return window.location = url;
                }
                
                this.html(data);

                // If there's a <title> tag in the response, use it as the page's title.
                title = this.find('title').remove().text();
            }

            if (title) {
                document.title = $.trim(title);
            }

            var state = {
                url: url,
                pjax: this.selector,
                fragment: options.fragment,
                timeout: options.timeout
            };
            
            if (options.replace) {
                pjax.active = true;
                window.history.replaceState(state, document.title, options['urlAddressBar'] || xhr.getResponseHeader('X-FinalURL') || url);
            } else if (options.push) {
                // this extra replaceState before first push ensures good back button behavior
                if (!pjax.active) {
                    window.history.replaceState($.extend({}, state, {url: null}), oldTitle);
                    pjax.active = true;
                }
                window.history.pushState(state, document.title, options['urlAddressBar'] || xhr.getResponseHeader('X-FinalURL') || url);
            }
            
            // Google Analytics support
            if ((options.replace || options.push) && window._gaq) {
                _gaq.push(['_trackPageview']);
            }

            // If the URL has a hash in it, make sure the browser knows to navigate to the hash.
            if (hash !== '') {
                window.location.href = hash;
            }

            fire('pjax:success', [data, status, xhr, options]);
        };


        // Cancel the current request if we're already pjaxing
        var xhr = pjax.xhr;
        if (xhr && xhr.readyState < 4) {
            xhr.onreadystatechange = $.noop;
            xhr.abort();
        }

//        options.statusCode = {
//            404: function() {
//                // alert('Not Found');
//            }
//        };
        
        pjax.options = options;
        pjax.xhr = $.ajax(options);
        $(document).trigger('pjax', [pjax.xhr, options]);

        return pjax.xhr;
    };

    function optionsFor(container, options) {
        // Both container and options
        if (container && options) {
            options.container = container;
        } else {
            if ($.isPlainObject(container)) {
                // First argument is options Object
                options = container;
            } else {
                // Only container
                options = {container: container};
            }
        }
        
        // Find and validate container
        if (options.container) {
            options.container = findContainerFor(options.container);
        }

        return options;
    }
    
    function findContainerFor(container) {
        container = $(container);

        if (!container.length) {
            throw "no pjax container for " + container.selector;
        } else if (container.selector !== '' && container.context === document) {
            return container;
        } else if (container.attr('id')) {
            return $('#' + container.attr('id'));
        } else {
            throw "cant get selector for pjax container!";
        }
    }

    pjax.defaults = {
        timeout: 0,
        push: true,
        replace: false,
        // We want the browser to maintain two separate internal caches: 
        //     one for pjax'd partial page loads and one for normal page loads. 
        // Without adding this secret parameter, some browsers will often confuse the two.
        data: {},
        type: 'GET',
        dataType: 'html'
    };

    pjax.click = handleClick;

    // Used to detect initial (useless) popstate.
    // If history.state exists, assume browser isn't going to fire initial popstate.
    var popped = ('state' in window.history), initialURL = location.href;

    // popstate handler takes care of the back and forward buttons
    // 
    // You probably shouldn't use pjax on pages with other pushState stuff yet.
    $(window).bind('popstate', function(event) {
        // Ignore inital popstate that some browsers fire on page load
        var initialPop = (!popped && location.href === initialURL);
        popped = true;
        if (initialPop) {
            return;
        }

        var state = event.state;

        if (state && state.pjax) {
            var container = state.pjax;
            if ($(container + '').length) {
                $.pjax({
                    url: state.url || location.href,
                    fragment: state.fragment,
                    container: container,
                    push: false,
                    timeout: state.timeout
                });
            } else {
                window.location = location.href;
            }
        }
    });

    // Add the state property to jQuery's event object so we can use it in
    // $(window).bind('popstate')
    if ($.inArray('state', $.event.props) < 0) $.event.props.push('state');

    // Is pjax supported by this browser?
    $.support.pjax = 
            window.history && window.history.pushState && window.history.replaceState
            // pushState isn't reliable on iOS until 5.
            && !navigator.userAgent.match(/((iPod|iPhone|iPad).+\bOS\s+[1-4]|WebApps\/.+CFNetwork)/);

    // Fall back to normal behavior for older browsers.
    if (!$.support.pjax) {
        $.pjax = function(options) {
            var url = $.isFunction(options.url) ? options.url() : options.url,
                method = options.type ? options.type.toUpperCase() : 'GET';

            var form = $('<form>', {
                method: method === 'GET' ? 'GET' : 'POST',
                action: url,
                style: 'display:none'
            });

            if (method !== 'GET' && method !== 'POST') {
                form.append($('<input>', {
                    type: 'hidden',
                    name: '_method',
                    value: method.toLowerCase()
                }));
            }

            var data = options.data;
            if (typeof data === 'string') {
                $.each(data.split('&'), function(index, value) {
                    var pair = value.split('=');
                    form.append($('<input>', {type: 'hidden', name: pair[0], value: pair[1]}));
                });
            } else if (typeof data === 'object') {
                for (key in data)
                    form.append($('<input>', {type: 'hidden', name: key, value: data[key]}));
            }

            $(document.body).append(form);
            form.submit();
        };
        $.pjax.click = $.noop;
        $.fn.pjax = function() {
            return this;
        };
    }

})(jQuery);

var pjax = function(href, container, urlAddressBar, noPjaxHeader) {
    if (typeof PjaxBeforeLoad === 'function') PjaxBeforeLoad();
    var options = (typeof href === 'object')? href : {
        url: href || window.location, 
        fragment: null
    };
    options['container'] = options['container'] || container || PjaxDefaultContainer || 'body';
    if (noPjaxHeader) {
        options['noPjaxHeader'] = true;
    }
    if (urlAddressBar === false) {
        options['urlAddressBar'] = window.location.href;
    }
    if (urlAddressBar) {
        options['urlAddressBar'] = urlAddressBar;
    }
    $.pjax(options);
};

var pjaxPost = function(href, data, container, urlAddressBar, noPjaxHeader) {
    pjax({url: href, data:data, type:'POST'}, container, urlAddressBar, noPjaxHeader);
};

var pjaxForm = function(form, data, container, urlAddressBar, noPjaxHeader) {
    event.preventDefault();
    form = document.forms[form] || $(form)[0] || form;
    data = (typeof formValuesToArray === 'function'? formValuesToArray(form) : data) || data || {};
    pjaxPost(form.action, data, container, urlAddressBar, noPjaxHeader);
    return false;
};

